#include "DynamicConverter.h"

#include <stdexcept>

namespace dynamic {

Value dynamicToValue(Runtime& runtime, const Dynamic& dyn) {
  switch (dyn.getType()) {
    case Dynamic::Type::Null:
      return Value::null();

    case Dynamic::Type::Bool:
      return Value(dyn.getBool());

    case Dynamic::Type::Double:
      return Value(dyn.getDouble());

    case Dynamic::Type::Int64:
      return Value((double)dyn.getInt64());

    case Dynamic::Type::String:
      return String::createFromUtf8(runtime, dyn.getString());

    case Dynamic::Type::Array: {
      auto array = dyn.getArray();
      auto size = array.size();
      Array ret(runtime, size);
      for (int i = 0; i < size; i++) {
        ret.setValueAtIndex(runtime, i, dynamicToValue(runtime, array[i]));
      }
      return ret;
    }

    case Dynamic::Type::Object: {
      Object ret(runtime);
      for (const auto& field : dyn.getObject()) {
        ret.setProperty(runtime, PropNameID::forUtf8(runtime, field.first), dynamicToValue(runtime, field.second));
      }
      return ret;
    }
  }
}

Dynamic valueToDynamic(Runtime& runtime, const Value& value) {
  if (value.isNull() || value.isUndefined()) {
    return Dynamic();
  }
  if (value.isBool()) {
    return Dynamic(value.getBool());
  }
  if (value.isNumber()) {
    return Dynamic(value.getNumber());
  }
  if (value.isString()) {
    return Dynamic(value.getString(runtime).utf8(runtime));
  }
  if (value.isObject()) {
    auto obj = value.getObject(runtime);
    if (obj.isArray(runtime)) {
      auto array = obj.getArray(runtime);
      auto size = array.size(runtime);
      std::vector<Dynamic> ret(size);
      for (int i = 0; i < size; i++) {
        ret.push_back(valueToDynamic(runtime, array.getValueAtIndex(runtime, i)));
      }
      return ret;
    } else {
      auto propNames = obj.getPropertyNames(runtime);
      auto size = propNames.size(runtime);
      std::map<std::string, Dynamic> ret;
      for (int i = 0; i < size; i++) {
        auto name = propNames.getValueAtIndex(runtime, i).asString(runtime).utf8(runtime);
        ret[name] = valueToDynamic(runtime, obj.getProperty(runtime, name.c_str()));
      }
      return ret;
    }
  }
  throw std::runtime_error("unsupported type");
}

}  // namespace dynamic